<script setup lang="ts">
import * as Cesium from 'cesium'
import type { Entity, CustomDataSource, Cartesian2 } from 'cesium'
import dayjs from 'dayjs'
import type { DataTableColumns, DataTableRowKey } from 'naive-ui'
import Orbit from '@/utils/Orbit'
import { useSatelliteStore } from '@/store'
import beidousatellites from '@/data/beidousatellites.json'
import TLES from '@/data/TLES.json'
import satellitepng from '@/assets/images/satellite.png'

interface RowData {
  key: number
  name: string
  IntCode: string
  Launchdate: string
}

interface SatelliteEntity extends Entity {
  oribit?: Orbit
  satelliteInfo?: Satellite
}

const satelliteStore = useSatelliteStore()
const BeidouList: SatelliteData = beidousatellites
const tlesData: TLES = TLES
const columns = createColumns()
const selectIndex = ref<DataTableRowKey>()
const heightscale = 0.1
let _dataSource: CustomDataSource
let _satelliteEntities: { [key: string]: SatelliteEntity | null } = {}
let highlightedEntity: SatelliteEntity

function handleCheck(
  _: DataTableRowKey[],
  rows: object[],
  meta: { row: object | undefined; action: 'check' | 'uncheck' | 'checkAll' | 'uncheckAll' }
) {
  const satellitesRows = rows as Satellite[]
  const selectedSatellitesRow = meta.row as Satellite

  switch (meta.action) {
    case 'check':
      if (satellitesRows.length > 0 && !_satelliteEntities[`${selectedSatellitesRow.NORADID}`]) {
        const { nowOrbit, nowPosition } = handleSatellite(selectedSatellitesRow)
        satelliteStore.setSatelliteData({
          name: selectedSatellitesRow.name,
          satnum: nowOrbit?.satnum,
          Launchdate: selectedSatellitesRow.Launchdate,
          period: nowOrbit?.orbitalPeriod,
          position: nowPosition,
          satelliteItem: selectedSatellitesRow
        })
      }
      break
    case 'uncheck':
      if (selectedSatellitesRow && _satelliteEntities[`${selectedSatellitesRow.NORADID}`]) {
        _dataSource.entities.remove(_satelliteEntities[`${selectedSatellitesRow.NORADID}`]!)
        _satelliteEntities[`${selectedSatellitesRow.NORADID}`] = null
      }
      break
    case 'checkAll':
      satellitesRows.forEach((item) => {
        if (!_satelliteEntities[`${item.NORADID}`]) {
          handleSatellite(item)
        }
      })
      break
    case 'uncheckAll':
      if (satellitesRows.length === 0) {
        _dataSource.entities.removeAll()
        _satelliteEntities = {}
      }
      break
  }
}

function handleClick(satelliteRow: Satellite, index: DataTableRowKey) {
  return {
    onClick: () => {
      if (_satelliteEntities[`${satelliteRow.NORADID}`]) {
        selectIndex.value = index

        const entity: SatelliteEntity = _satelliteEntities[`${satelliteRow.NORADID}`]!
        if (entity) {
          if (highlightedEntity && highlightedEntity !== entity) {
            highlightedEntity.billboard!.color = new Cesium.ConstantProperty(Cesium.Color.WHITE)
            highlightedEntity.label!.fillColor = new Cesium.ConstantProperty(new Cesium.Color(0.894, 0.933, 0.902, 1.0))
          }
          entity.billboard!.color = new Cesium.ConstantProperty(new Cesium.Color(0.0118, 0.2941, 0.6353, 1.0))
          entity.label!.fillColor = new Cesium.ConstantProperty(new Cesium.Color(0.0118, 0.2941, 0.6353, 1.0))
          satelliteStore.setSatelliteData({
            name: entity.name as string,
            satnum: entity.oribit?.satnum as string,
            Launchdate: entity.satelliteInfo?.Launchdate as string,
            period: entity.oribit?.orbitalPeriod as number,
            position: entity.oribit?.positionGeodetic(dayjs().toDate()) as GeoPosition,
            satelliteItem: entity.satelliteInfo!
          })
          highlightedEntity = entity
        }

        const nowOrbit = new Orbit(satelliteRow.name, satelliteRow.TLE!)
        const nowPosition: GeoPosition = nowOrbit.positionGeodetic(dayjs().toDate())
        satelliteStore.setSatelliteData({
          name: satelliteRow.name,
          satnum: nowOrbit?.satnum,
          Launchdate: satelliteRow.Launchdate,
          period: nowOrbit?.orbitalPeriod,
          position: nowPosition,
          satelliteItem: satelliteRow
        })
      }
    },
    onDblclick: () => {
      if (_satelliteEntities[`${satelliteRow.NORADID}`]) {
        const nowOrbit = new Orbit(satelliteRow.name, satelliteRow.TLE!)
        const nowPosition: GeoPosition = nowOrbit.positionGeodetic(dayjs().toDate())
        window.viewer.camera.flyTo({
          destination: Cesium.Cartesian3.fromRadians(nowPosition.longitude, nowPosition.latitude, nowPosition.height)
        })
      }
    }
  }
}

function handleSatellite(satelliteRow: Satellite): { nowOrbit: Orbit; nowPosition: GeoPosition } {
  const nowOrbit = new Orbit(satelliteRow.name, satelliteRow.TLE!)
  const nowPosition: GeoPosition = nowOrbit.positionGeodetic(dayjs().toDate())
  const satellite = createSatellitEntity(satelliteRow, nowPosition)
  satellite.oribit = nowOrbit
  satellite.satelliteInfo = satelliteRow
  _satelliteEntities[`${satelliteRow.NORADID}`] = satellite

  window.viewer.camera.flyTo({
    destination: Cesium.Cartesian3.fromRadians(nowPosition.longitude, nowPosition.latitude, nowPosition.height)
  })
  return { nowOrbit, nowPosition }
}

function createColumns(): DataTableColumns<RowData> {
  return [
    { type: 'selection' },
    { title: '名称', key: 'name', width: 90 },
    { title: '编号', key: 'IntCode', width: 75 },
    { title: '发射时间', key: 'Launchdate', width: 80 }
  ]
}

function changeClass(_: object, index: DataTableRowKey) {
  if (selectIndex.value == index) {
    return 'selected'
  } else {
    return ''
  }
}

function linkTLEToSatellites() {
  const tlslist: TlsList = {}
  const temp = tlesData.list[0].split('\n')
  let line1: string = ''
  let line2: string = ''

  temp.forEach((tle, index) => {
    if (index % 2 == 0) {
      line1 = tle
    }
    if (index % 2 == 1) {
      line2 = tle
      tlslist[`${line2.split(' ')[1].trim()}`] = `${line1}\n${line2}`
    }
  })
  BeidouList.data.forEach((item: Satellite) => {
    if (tlslist[`${item.NORADID}`]) {
      const tle = tlslist[`${item.NORADID}`]
      item.TLE = `${item.name}\n${tle}`
    }
  })
}

function createSatellitEntity(item: Satellite, position: GeoPosition): SatelliteEntity {
  const satellite: SatelliteEntity = _dataSource.entities.add({
    position: Cesium.Cartesian3.fromRadians(position.longitude, position.latitude, position.height * heightscale),
    id: `satellite-${item.NORADID}`,
    name: item.name,
    label: {
      text: item.name,
      font: '14px',
      fillColor: new Cesium.Color(0.894, 0.933, 0.902, 1.0)
    },
    billboard: {
      image: satellitepng,
      width: 24,
      height: 24,
      verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
      pixelOffset: new Cesium.Cartesian2(0, -10),
      eyeOffset: new Cesium.Cartesian3(0, 0, 100)
    },
    cylinder: {
      topRadius: 0,
      bottomRadius: 1000000,
      length: position.height * heightscale,
      heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
      material: new Cesium.Color(1, 0, 0, 0.6)
    }
  })
  return satellite
}

function pickSatellite() {
  const pickStationHandler = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas)
  pickStationHandler.setInputAction((event: { position: Cartesian2 }) => {
    const feature = window.viewer.scene.pick(event.position)
    if (Cesium.defined(feature)) {
      if (Object.prototype.hasOwnProperty.call(feature, 'id') && feature.id instanceof Cesium.Entity) {
        if (feature.id._id.startsWith('satellite-')) {
          const name = feature.id._id.substring(10, feature.id._id.length)

          const entity: SatelliteEntity = _satelliteEntities[`${name}`]!
          if (highlightedEntity && highlightedEntity !== entity) {
            highlightedEntity.billboard!.color = new Cesium.ConstantProperty(Cesium.Color.WHITE)
            highlightedEntity.label!.fillColor = new Cesium.ConstantProperty(new Cesium.Color(0.894, 0.933, 0.902, 1.0))
          }
          const index = BeidouList.data.findIndex((item) => item.name === entity.name)
          selectIndex.value = index
          entity.billboard!.color = new Cesium.ConstantProperty(new Cesium.Color(0.0118, 0.2941, 0.6353, 1.0))
          entity.label!.fillColor = new Cesium.ConstantProperty(new Cesium.Color(0.0118, 0.2941, 0.6353, 1.0))
          satelliteStore.setSatelliteData({
            name: entity.name as string,
            satnum: entity.oribit?.satnum as string,
            Launchdate: entity.satelliteInfo?.Launchdate as string,
            period: entity.oribit?.orbitalPeriod as number,
            position: entity.oribit?.positionGeodetic(dayjs().toDate()) as GeoPosition,
            satelliteItem: entity.satelliteInfo!
          })
          highlightedEntity = entity
        }
      }
    } else {
      if (highlightedEntity) {
        highlightedEntity.label!.fillColor = new Cesium.ConstantProperty(new Cesium.Color(0.894, 0.933, 0.902, 1.0))
        highlightedEntity.billboard!.color = new Cesium.ConstantProperty(Cesium.Color.WHITE)
      }
    }
  }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
}

onMounted(() => {
  _dataSource = new Cesium.CustomDataSource('Satellites')
  window.viewer.dataSources.add(_dataSource)
  linkTLEToSatellites()
  pickSatellite()
  // 每秒更新一次所有显示卫星的位置
  window.setInterval(() => {
    const keys = Object.keys(_satelliteEntities)
    if (keys && keys.length > 0) {
      keys.forEach((key) => {
        const entity = _satelliteEntities[`${key}`]
        if (entity) {
          const nowPosition: GeoPosition = entity.oribit!.positionGeodetic(dayjs().toDate())
          entity.position = new Cesium.ConstantPositionProperty(
            Cesium.Cartesian3.fromRadians(nowPosition.longitude, nowPosition.latitude, nowPosition.height * heightscale)
          )
        }
      })
    }
  }, 1000)
})
</script>

<template>
  <n-space vertical size="large">
    <div class="list-panel">
      <n-data-table
        max-height="77vh"
        class="table-panel"
        :columns="columns"
        :data="BeidouList.data"
        :row-key="(row: RowData) => row.IntCode"
        style="width: 19vw"
        header-align="center"
        :row-props="handleClick"
        :row-class-name="changeClass"
        @update:checked-row-keys="handleCheck" />
    </div>
  </n-space>
</template>

<style lang="scss" scoped>
.list-panel {
  margin-bottom: 2vh;
  :deep(.n-data-table th) {
    text-align: center;
  }
  :deep(.selected td) {
    background-color: #18a058 !important;
  }
  .__data-table-dark-m {
    -webkit-user-select: none; /* Chrome, Safari, Opera */
    -moz-user-select: none; /* Firefox */
    -ms-user-select: none; /* Internet Explorer/Edge */
    user-select: none; /* Non-prefixed version, currently supported by Chrome and Opera */
    --n-th-padding: 8px;
    --n-td-padding: 8px;
    --n-th-color: #034ba27c;
    --n-td-color: transparent;
    --n-td-color-hover: #2a3342;
  }
}
</style>
